/* Copyright Statement:
 *
 * This software/firmware and related documentation ("AutoChips Software") are
 * protected under relevant copyright laws. The information contained herein is
 * confidential and proprietary to AutoChips Inc. and/or its licensors. Without
 * the prior written permission of AutoChips inc. and/or its licensors, any
 * reproduction, modification, use or disclosure of AutoChips Software, and
 * information contained herein, in whole or in part, shall be strictly
 * prohibited.
 *
 * AutoChips Inc. (C) 2023. All rights reserved.
 *
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("AUTOCHIPS SOFTWARE")
 * RECEIVED FROM AUTOCHIPS AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
 * ON AN "AS-IS" BASIS ONLY. AUTOCHIPS EXPRESSLY DISCLAIMS ANY AND ALL
 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NONINFRINGEMENT. NEITHER DOES AUTOCHIPS PROVIDE ANY WARRANTY WHATSOEVER WITH
 * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
 * INCORPORATED IN, OR SUPPLIED WITH THE AUTOCHIPS SOFTWARE, AND RECEIVER AGREES
 * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
 * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
 * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN AUTOCHIPS
 * SOFTWARE. AUTOCHIPS SHALL ALSO NOT BE RESPONSIBLE FOR ANY AUTOCHIPS SOFTWARE
 * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND AUTOCHIPS'S
 * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE AUTOCHIPS SOFTWARE
 * RELEASED HEREUNDER WILL BE, AT AUTOCHIPS'S OPTION, TO REVISE OR REPLACE THE
 * AUTOCHIPS SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
 * CHARGE PAID BY RECEIVER TO AUTOCHIPS FOR SUCH AUTOCHIPS SOFTWARE AT ISSUE.
 */

/*!
 * @file ac780x_i2c.c
 *
 * @brief This file provides i2c integration functions.
 *
 */

/* ===========================================  Includes  =========================================== */
#include "ac780x_i2c_reg.h"
#include "ac780x_dma_reg.h"

/* ============================================  Define  ============================================ */

/* ===========================================  Typedef  ============================================ */

/* ==========================================  Variables  =========================================== */
/* i2c info */
const I2C_INFOType g_i2cInfoPool[] =
{
    {I2C0, I2C0_IRQn, CLK_I2C0, SRST_I2C0},
    {I2C1, I2C1_IRQn, CLK_I2C1, SRST_I2C1},
};
/* I2C callback pointer array */
static DeviceCallback_Type s_i2cCallback[I2C_INSTANCE_MAX] = {(DeviceCallback_Type)NULL};

#ifdef I2C_USE_INTERRUPT_TRANSMIT

/* transmit information struct */
I2C_TransmitType g_i2cTransmitInfo[I2C_INSTANCE_MAX] = {0U};
/* transmit status enum */
uint32_t g_i2cStatus[I2C_INSTANCE_MAX] = {I2C_TRANSMIT_STATUS_ERROR_NULL};
/* internal callback function */
static void I2C_MasterSlaveIntHandler(void *device, uint32_t wparam, uint32_t lparam);

#endif /* I2C_USE_INTERRUPT_TRANSMIT */

/* ====================================  Functions declaration  ===================================== */

/* ======================================  Functions define  ======================================== */
/*!
 * @brief Initialize I2C module.
 *
 * @param[in] I2Cx: i2c module,x can be 0.1
 * @param[in] config: I2C configuration pointer
 * @return none
 */
void I2C_Init(I2C_Type *I2Cx, const I2C_ConfigType *config)
{
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);
    DEVICE_ASSERT(NULL != config);

    /* i2c module system enable */
    CKGEN_Enable(g_i2cInfoPool[instance].CKGEN_I2Cx, ENABLE);
    CKGEN_SoftReset(g_i2cInfoPool[instance].SRST_I2Cx, ENABLE);

    /* master scl sample, halfbaud setting */
    I2C_SetSampleStep(I2Cx, config->masterConfig.sampleCnt, config->masterConfig.stepCnt);

    /* master scl sync, arbitration */
    I2C_SetSYNC(I2Cx, config->masterConfig.SYNCEn);
    I2C_SetARB(I2Cx, config->masterConfig.ARBEn);

    /* slave 7bit, 7bit range address */
    I2C_SetRAD(I2Cx, config->slaveConfig.rangeAddrEn);
    I2C_SetSlaveAddr(I2Cx, config->slaveConfig.slaveAddr);
    I2C_SetSlaveRangeAddr(I2Cx, config->slaveConfig.slaveRangeAddr);

    /* master/slave 10bit address */
    I2C_SetADEXT(I2Cx, config->extAddrEn);

    /* slave stretch */
    I2C_SetStretch(I2Cx, config->slaveConfig.stretchEn);

    /* slave monitor */
    I2C_SetMNT(I2Cx, config->slaveConfig.monitorEn);

    /* slave general call */
    I2C_SetGCA(I2Cx, config->slaveConfig.generalCallEn);

    /* slave wakeup */
    I2C_SetWakeup(I2Cx, config->slaveConfig.wakeupEn);

    /* tx underflow, rx overflow int */
    I2C_SetTxUFInterrupt(I2Cx, config->slaveConfig.txUFInterruptEn);
    I2C_SetRxOFInterrupt(I2Cx, config->slaveConfig.rxOFInterruptEn);

    /* i2c deglitch cnt */
    I2C_SetDGLCnt(I2Cx, config->slaveConfig.glitchFilterCnt);

    /* i2c start stop int */
    I2C_SetSSInterrupt(I2Cx, config->ssInterruptEn);

    /* i2c tx by DMA */
    I2C_SetDMATx(I2Cx, config->dmaTxEn);

    /* i2c rx by DMA */
    I2C_SetDMARx(I2Cx, config->dmaRxEn);

    /* i2c nack int */
    I2C_SetNackInterrupt(I2Cx, config->nackInterruptEn);

    /* i2c master/slave */
    I2C_SetMSTR(I2Cx, config->mode);

    /* i2c module enable */
    I2C_SetModuleEnable(I2Cx, config->i2cEn);

    /* i2c callback function */
    s_i2cCallback[instance] = config->callBack;

    /* i2c int */
    I2C_SetInterrupt(I2Cx, config->interruptEn);

    /* i2c NVIC */
    if (DISABLE == config->interruptEn)
    {
        NVIC_DisableIRQ(g_i2cInfoPool[instance].I2Cx_IRQn);
        NVIC_ClearPendingIRQ(g_i2cInfoPool[instance].I2Cx_IRQn);
    }
    else
    {
        NVIC_EnableIRQ(g_i2cInfoPool[instance].I2Cx_IRQn);
    }
}

/*!
 * @brief I2C De-initialize.
 *
 * @param[in] I2Cx: i2c module,x can be 0.1
 * @return none
 */
void I2C_DeInit(I2C_Type *I2Cx)
{
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);

    s_i2cCallback[instance] = (DeviceCallback_Type)NULL;
    NVIC_DisableIRQ(g_i2cInfoPool[instance].I2Cx_IRQn);
    NVIC_ClearPendingIRQ(g_i2cInfoPool[instance].I2Cx_IRQn);
    CKGEN_SoftReset(g_i2cInfoPool[instance].SRST_I2Cx, DISABLE);
    CKGEN_Enable(g_i2cInfoPool[instance].CKGEN_I2Cx, DISABLE);
}

/*!
 * @brief set I2C Callback function.
 *
 * @param[in] I2Cx: i2c module,x can be 0.1
 * @param[in] callback: call back function
 * @return none
 */
void I2C_SetCallback(I2C_Type *I2Cx, DeviceCallback_Type callback)
{
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);

    s_i2cCallback[instance] = callback;
}

/*!
 * @brief I2C0 interrupt request handler.
 *
 * @param[in] none
 * @return none
 */
void I2C0_IRQHandler(void)
{
    uint32_t wpara = 0U, lpara = 0U;

    wpara = I2C_GetStatus0(I2C0);
    lpara = I2C_GetStatus1(I2C0);

#ifdef I2C_USE_INTERRUPT_TRANSMIT
    if (DISABLE == I2C_GetMNT(I2C0))
    {
        I2C_MasterSlaveIntHandler(I2C0, wpara, lpara);
    }
#endif /* I2C_USE_INTERRUPT_TRANSMIT */

    if (NULL != s_i2cCallback[0U])
    {
        s_i2cCallback[0U](I2C0, wpara, lpara);
    }
}

/*!
 * @brief I2C1 interrupt request handler.
 *
 * @param[in] none
 * @return none
 */
void I2C1_IRQHandler(void)
{
    uint32_t wpara = 0U, lpara = 0U;

    wpara = I2C_GetStatus0(I2C1);
    lpara = I2C_GetStatus1(I2C1);

#ifdef I2C_USE_INTERRUPT_TRANSMIT
    if (DISABLE == I2C_GetMNT(I2C1))
    {
        I2C_MasterSlaveIntHandler(I2C1, wpara, lpara);
    }
#endif /* I2C_USE_INTERRUPT_TRANSMIT */

    if (NULL != s_i2cCallback[1U])
    {
        s_i2cCallback[1U](I2C1, wpara, lpara);
    }
}

/*!
 * @brief I2C send start.
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @return i2c hardware status
 */
uint32_t I2C_Start(I2C_Type *I2Cx)
{
    uint32_t timeout = 0U;
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));

    I2C_TxEn(I2Cx);
    I2C_SendStart(I2Cx);

    /* wait busy status */
    while ((!I2C_IsBusy(I2Cx)) && (I2C_HW_DEADLINE_TIMEOUT > timeout++));
    if (I2C_HW_DEADLINE_TIMEOUT <= timeout)
    {
        status |= I2C_HW_STATUS_START_TO_BUSY_TIMEOUT;
    }
    I2C_ClearStartFlag(I2Cx);

    /* wait core ready */
    timeout = 0U;
    while ((!I2C_IsReady(I2Cx)) && (I2C_HW_DEADLINE_TIMEOUT > timeout++));
    if (I2C_HW_DEADLINE_TIMEOUT <= timeout)
    {
        status |= I2C_HW_STATUS_CORE_READY_TIMROUT;
    }

    return status;
}

/*!
 * @brief I2C send re-start,make sure to disable start/stop interrupt when use the interface.
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @return i2c hardware status
 */
uint32_t I2C_Restart(I2C_Type *I2Cx)
{
    uint32_t timeout = 0U;
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));

    I2C_ClearStartFlag(I2Cx);
    I2C_SendStart(I2Cx);

    /* wait start status */
    while ((!I2C_IsStart(I2Cx)) && (I2C_HW_DEADLINE_TIMEOUT > timeout++));
    if (I2C_HW_DEADLINE_TIMEOUT <= timeout)
    {
        status |= I2C_HW_STATUS_START_TO_BUSY_TIMEOUT;
    }
    I2C_ClearStartFlag(I2Cx);

    /* wait core ready */
    timeout = 0U;
    while ((!I2C_IsReady(I2Cx)) && (I2C_HW_DEADLINE_TIMEOUT > timeout++));
    if (I2C_HW_DEADLINE_TIMEOUT <= timeout)
    {
        status |= I2C_HW_STATUS_CORE_READY_TIMROUT;
    }

    return status;
}

/*!
 * @brief I2C send stop.
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @return i2c hardware status
 */
uint32_t I2C_Stop(I2C_Type *I2Cx)
{
    uint32_t timeout = 0U;
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));

    I2C_TxEn(I2Cx);
    I2C_SendStop(I2Cx);

    /* wait idle status */
    while (I2C_IsBusy(I2Cx) && (I2C_HW_DEADLINE_TIMEOUT > timeout++));
    if (I2C_HW_DEADLINE_TIMEOUT <= timeout)
    {
        status |= I2C_HW_STATUS_STOP_TO_IDLE_TIMEOUT;
    }
    I2C_ClearStopFlag(I2Cx);

    /* wait core ready */
    timeout = 0U;
    while ((!I2C_IsReady(I2Cx)) && (I2C_HW_DEADLINE_TIMEOUT > timeout++));
    if (I2C_HW_DEADLINE_TIMEOUT <= timeout)
    {
        status |= I2C_HW_STATUS_CORE_READY_TIMROUT;
    }

    return status;
}

/*!
 * @brief wait one byte transmit finished.
 *        if use Master DMA transmit, user must call this API when DMA transmit finished, then user decided to send stop
 *        if use Slave DMA transmit, it's recommended that the user call this API to clear some status
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @return i2c hardware status
 */
uint32_t I2C_WaitOneByteFinished(I2C_Type *I2Cx)
{
    uint32_t timeout = 0U, tmpReg = 0U;
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    tmpReg = I2C_GetStatus0(I2Cx);
    while ((!(tmpReg & I2C_STATUS0_BND_Msk)) && (I2C_HW_DEADLINE_TIMEOUT > timeout++))
    {
        tmpReg = I2C_GetStatus0(I2Cx);
    }
    if (tmpReg & I2C_STATUS0_RACK_Msk)
    {
        I2C_ClearStatus0(I2Cx, I2C_STATUS0_RACK_Msk);
        status |= I2C_HW_STATUS_NACK;
    }
    if (tmpReg & I2C_STATUS0_BND_Msk)
    {
        I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);
    }
    if (I2C_HW_DEADLINE_TIMEOUT <= timeout)
    {
        status |= I2C_HW_STATUS_BND_TIMEOUT;
    }

    return status;
}

/*!
 * @brief I2C write one byte with polling BND flag,make sure to disable interrupt when use the interface.
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @param[in] data: data to write
 * @return i2c hardware status
 */
uint32_t I2C_TransmitOneByte(I2C_Type *I2Cx, uint8_t data)
{
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));

    I2C_TxEn(I2Cx);
    I2C_WriteDataReg(I2Cx, data);
    status = I2C_WaitOneByteFinished(I2Cx);

    return status;
}

/*!
 * @brief I2C read one byte and send next clock,make sure to disable interrupt when use the interface.
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @param[in] data: data to read
 * @param[in] nack: the next byte ack status
 * @return i2c hardware status
 */
uint32_t I2C_ReceiveOneByte(I2C_Type *I2Cx, uint8_t *data, I2C_AckType nack)
{
    uint32_t timeout = 0U;
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));
    DEVICE_ASSERT(NULL != data);

    I2C_RxEn(I2Cx);
    if (I2C_ACK == nack)
    {
        I2C_SendAck(I2Cx);
    }
    else
    {
        I2C_SendNack(I2Cx);
    }
    *data = I2C_ReadDataReg(I2Cx);

    while ((!(I2C_GetStatus0(I2Cx) & I2C_STATUS0_BND_Msk)) && (I2C_HW_DEADLINE_TIMEOUT > timeout++));
    if (I2C_HW_DEADLINE_TIMEOUT <= timeout)
    {
        status |= I2C_HW_STATUS_BND_TIMEOUT;
    }

    return status;
}

/*!
 * @brief I2C read one byte without send next clock.
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @param[in] data: data to read
 * @return none
 */
void I2C_ReceiveLastOneByte(I2C_Type *I2Cx, uint8_t *data)
{
    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));

    /* change direction to avoid send out next byte clock */
    I2C_TxEn(I2Cx);

    *data = I2C_ReadDataReg(I2Cx);

    /* when direction is Tx, read data won't clear BND flag instead of write clear */
    I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);
}

/*!
 * @brief I2C get internal hardware ready status.
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @return i2c hardware status
 */
uint32_t I2C_WaitForReady(I2C_Type *I2Cx)
{
    uint32_t timeout = 0U;
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));

    while ((!(I2C_GetStatus0(I2Cx) & I2C_STATUS0_READY_Msk)) && (I2C_HW_DEADLINE_TIMEOUT > timeout++));
    if (I2C_HW_DEADLINE_TIMEOUT <= timeout)
    {
        status |= I2C_HW_STATUS_CORE_READY_TIMROUT;
    }

    return status;
}

/*!
 * @brief I2C burst write with 7bit/10bit addressing,make sure to disable interrupt when use the interface.
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @param[in] slaveAddr: slave 7bit/10bit address
 * @param[in] buff: the data buffer
 * @param[in] dataLength: the length to write
 * @param[in] stopEn: enable/diable i2c send stop after lastone byte for a possible re-start
 * @return i2c hardware status
 */
uint32_t I2C_MasterTransmitPoll(I2C_Type *I2Cx, uint16_t slaveAddr, uint8_t *buff, uint32_t dataLength, ACTION_Type stopEn)
{
    uint32_t i = 0U;
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));
    DEVICE_ASSERT(NULL != buff);

    status |= I2C_WaitForReady(I2Cx);
    status |= I2C_Start(I2Cx);
    if (!I2C_IsADEXT(I2Cx))
    {
        status |= I2C_TransmitOneByte(I2Cx, ((uint8_t)slaveAddr << 1U) | (uint8_t)I2C_WRITE);
    }
    else
    {
        status |= I2C_TransmitOneByte(I2Cx, ((I2C_ADDEXT_PRIMARY_BYTE_FIX + (slaveAddr >> 8U)) << 1U) | (uint8_t)I2C_WRITE);
        status |= I2C_TransmitOneByte(I2Cx, (uint8_t)slaveAddr);
    }
    if (I2C_HW_STATUS_ERROR_NULL == status)
    {
        for (i = 0U; i < dataLength; i++)
        {
            status |= I2C_TransmitOneByte(I2Cx, buff[i]);
            if (I2C_HW_STATUS_ERROR_NULL != status)
            {
                break;
            }
        }
        if (ENABLE == stopEn)
        {
            status |= I2C_Stop(I2Cx);
        }
    }
    else
    {
        status |= I2C_Stop(I2Cx);
    }

    return status;
}

/*!
 * @brief I2C burst read with 7bit/10bit addressing,make sure to disable interrupt when use the interface.
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @param[in] slaveAddr: slave 7bit/10bit address
 * @param[in] buff: the data buffer
 * @param[in] dataLength: the length to read
 * @return i2c hardware status
 */
uint32_t I2C_MasterReceivePoll(I2C_Type *I2Cx, uint16_t slaveAddr, uint8_t *buff, uint32_t dataLength)
{
    uint32_t i = 0U;
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));
    DEVICE_ASSERT(NULL != buff);
    DEVICE_ASSERT(0U < dataLength);

    status |= I2C_WaitForReady(I2Cx);
    status |= I2C_Start(I2Cx);
    if (!I2C_IsADEXT(I2Cx))
    {
        status |= I2C_TransmitOneByte(I2Cx, ((uint8_t)slaveAddr << 1U) | (uint8_t)I2C_READ);
    }
    else
    {
        status |= I2C_TransmitOneByte(I2Cx, ((I2C_ADDEXT_PRIMARY_BYTE_FIX + (slaveAddr >> 8U)) << 1U) | (uint8_t)I2C_WRITE);
        status |= I2C_TransmitOneByte(I2Cx, (uint8_t)slaveAddr);
        status |= I2C_Restart(I2Cx);
        status |= I2C_TransmitOneByte(I2Cx, ((I2C_ADDEXT_PRIMARY_BYTE_FIX + (slaveAddr >> 8U)) << 1U) | (uint8_t)I2C_READ);
    }
    if (I2C_HW_STATUS_ERROR_NULL == status)
    {
        if (1 == dataLength)
        {
            status |= I2C_ReceiveOneByte(I2Cx, &buff[0U], I2C_NACK);
            I2C_ReceiveLastOneByte(I2Cx, &buff[0U]);
        }
        else
        {
            status |= I2C_ReceiveOneByte(I2Cx, &buff[0U], I2C_ACK);
            for (i = 0U; i < dataLength - 2U; i++)
            {
                status |= I2C_ReceiveOneByte(I2Cx, &buff[i], I2C_ACK);
                if (I2C_HW_STATUS_ERROR_NULL != status)
                {
                    break;
                }
            }
            if (I2C_HW_STATUS_ERROR_NULL == status)
            {
                status |= I2C_ReceiveOneByte(I2Cx, &buff[i++], I2C_NACK);
                I2C_ReceiveLastOneByte(I2Cx, &buff[i]);
            }
        }
    }
    status |= I2C_Stop(I2Cx);

    return status;
}

/*!
 * @brief I2C master start DMA transmit.
 *        this will initialize DMA channel with the general DMA config
 *        make sure to enable i2c dma tx in I2C_Init before use the interface
 *        master start DMA transmit only when slave address match
 *        master get slave address or data nack need enable nack interrupt first.
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @param[in] slaveAddr: 7/10bit slave address
 * @param[in] DMAx: DMA module
 * @param[in] txBuffer: memory start address
 * @param[in] length: length to transmit
 * @param[in] callback: the DMA interrupt callback function
 * @return i2c hardware status
 */
uint32_t I2C_MasterTransmitDMA(I2C_Type *I2Cx, uint16_t slaveAddr, DMA_ChannelType *DMAx,
                               uint8_t *txBuffer, uint16_t length, DeviceCallback_Type callback)
{
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;
    uint32_t userIntEnVal = I2C_IsInterruptEnable(I2Cx);
    DMA_ConfigType dmaConfig;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));
    DEVICE_ASSERT(IS_DMA_PERIPH(DMAx));
    DEVICE_ASSERT(IS_DMA_TRANSFER_NUM(length));

    if ((uint32_t)txBuffer & 0x3U)  /* txBuffer address is not word alignment */
    {
        return I2C_TRANSMIT_STATUS_ERROR_NULL;
    }

    dmaConfig.memStartAddr = (uint32_t)txBuffer;
    dmaConfig.memEndAddr = (uint32_t)txBuffer + ((1U + (length >> 2U)) << 2U);
    dmaConfig.periphStartAddr = (uint32_t)(&(I2Cx->DATA));
    dmaConfig.periphSelect = (I2C0 == I2Cx) ? DMA_PEPIRH_I2C0_TX : DMA_PEPIRH_I2C1_TX;
    dmaConfig.MEM2MEM = DISABLE;
    dmaConfig.memSize = DMA_MEM_SIZE_32BIT;
    dmaConfig.periphSize = DMA_PERIPH_SIZE_8BIT;
    dmaConfig.memIncrement = ENABLE;
    dmaConfig.periphIncrement = DISABLE;
    dmaConfig.direction = DMA_READ_FROM_MEM;
    dmaConfig.transferNum = length;
    dmaConfig.memByteMode = DMA_MEM_BYTE_MODE_4TIME;
    dmaConfig.circular = DISABLE;
    dmaConfig.channelPriority = DMA_PRIORITY_VERY_HIGH;
    dmaConfig.errorInterruptEn = ENABLE;
    dmaConfig.halfFinishInterruptEn = DISABLE;
    dmaConfig.finishInterruptEn = ENABLE;
    dmaConfig.channelEn = ENABLE;
    dmaConfig.callBack = callback;
    DMA_Init(DMAx, &dmaConfig);

    status |= I2C_WaitForReady(I2Cx);
    status |= I2C_Start(I2Cx);
    if (!I2C_IsADEXT(I2Cx))
    {
        I2C_TxEn(I2Cx);
        I2C_WriteDataReg(I2Cx, (((uint8_t)slaveAddr << 1U) | (uint8_t)I2C_WRITE));
    }
    else
    {
        I2C_SetInterrupt(I2Cx, DISABLE);
        I2C_SetDMATx(I2Cx, DISABLE);
        status |= I2C_TransmitOneByte(I2Cx, ((I2C_ADDEXT_PRIMARY_BYTE_FIX + (slaveAddr >> 8U)) << 1U) | (uint8_t)I2C_WRITE);
        I2C_SetDMATx(I2Cx, ENABLE);
        if (userIntEnVal)
        {
            I2C_SetInterrupt(I2Cx, ENABLE);
        }
        if (I2C_HW_STATUS_ERROR_NULL == status)
        {
            I2C_TxEn(I2Cx);
            I2C_WriteDataReg(I2Cx, (uint8_t)slaveAddr);
        }
    }

    return status;
}

/*!
 * @brief I2C master start DMA receive.
 *        this will initialize DMA channel with the general DMA config
 *        make sure to enable i2c dma rx in I2C_Init before use the interface
 *        master start DMA receive only when slave address match
 *        master get slave address nack need enable nack interrupt first
 *        DMA receive length must > 1
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @param[in] slaveAddr: 7/10bit slave address
 * @param[in] DMAx: DMA module
 * @param[in] rxBuffer: memory start address
 * @param[in] length: length to receive
 * @param[in] callback: the DMA interrupt callback function
 * @return i2c hardware status
 */
uint32_t I2C_MasterReceiveDMA(I2C_Type *I2Cx, uint16_t slaveAddr, DMA_ChannelType *DMAx,
                              uint8_t *rxBuffer, uint16_t length, DeviceCallback_Type callback)
{
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;
    DMA_ConfigType dmaConfig;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));
    DEVICE_ASSERT(IS_DMA_PERIPH(DMAx));
    DEVICE_ASSERT(IS_DMA_TRANSFER_NUM(length));

    dmaConfig.memStartAddr = (uint32_t)rxBuffer;
    dmaConfig.memEndAddr = (uint32_t)rxBuffer + length;
    dmaConfig.periphStartAddr = (uint32_t)(&(I2Cx->DATA));
    dmaConfig.periphSelect = (I2C0 == I2Cx) ? DMA_PEPIRH_I2C0_RX : DMA_PEPIRH_I2C1_RX;
    dmaConfig.MEM2MEM = DISABLE;
    dmaConfig.memSize = DMA_MEM_SIZE_8BIT;
    dmaConfig.periphSize = DMA_PERIPH_SIZE_32BIT;
    dmaConfig.memIncrement = ENABLE;
    dmaConfig.periphIncrement = DISABLE;
    dmaConfig.direction = DMA_READ_FROM_PERIPH;
    dmaConfig.transferNum = length;
    dmaConfig.memByteMode = DMA_MEM_BYTE_MODE_1TIME;
    dmaConfig.circular = DISABLE;
    dmaConfig.channelPriority = DMA_PRIORITY_VERY_HIGH;
    dmaConfig.errorInterruptEn = ENABLE;
    dmaConfig.halfFinishInterruptEn = DISABLE;
    dmaConfig.finishInterruptEn = ENABLE;
    dmaConfig.channelEn = ENABLE;
    dmaConfig.callBack = callback;
    DMA_Init(DMAx, &dmaConfig);

    status |= I2C_WaitForReady(I2Cx);
    status |= I2C_Start(I2Cx);
    if (!I2C_IsADEXT(I2Cx))
    {
        status |= I2C_TransmitOneByte(I2Cx, (uint8_t)((slaveAddr << 1U) | (uint8_t)I2C_READ));
    }
    else
    {
        status |= I2C_TransmitOneByte(I2Cx, ((I2C_ADDEXT_PRIMARY_BYTE_FIX + (slaveAddr >> 8U)) << 1U) | (uint8_t)I2C_WRITE);
        status |= I2C_TransmitOneByte(I2Cx, (uint8_t)slaveAddr);
        status |= I2C_Restart(I2Cx);
        status |= I2C_TransmitOneByte(I2Cx, ((I2C_ADDEXT_PRIMARY_BYTE_FIX + (slaveAddr >> 8U)) << 1U) | (uint8_t)I2C_READ);
    }
    if (I2C_HW_STATUS_ERROR_NULL == status)
    {
        I2C_RxEn(I2Cx);
        I2C_DumpReadDataReg(I2Cx);
    }
    else
    {
        status |= I2C_Stop(I2Cx);
    }

    return status;
}

/*!
 * @brief I2C slave start DMA transmit.
 *        this will initialize DMA channel with the general DMA config
 *        make sure to enable i2c dma tx in I2C_Init before use the interface
 *        slave start DMA transmit only when slave address match
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @param[in] DMAx: DMA module
 * @param[in] txBuffer: memory start address
 * @param[in] length: length to transmit
 * @param[in] callback: the DMA interrupt callback function
 * @return none
 */
void I2C_SlaveTransmitDMA(I2C_Type *I2Cx, DMA_ChannelType *DMAx, uint8_t *txBuffer, uint16_t length, DeviceCallback_Type callback)
{
    DMA_ConfigType dmaConfig;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));
    DEVICE_ASSERT(IS_DMA_PERIPH(DMAx));
    DEVICE_ASSERT(IS_DMA_TRANSFER_NUM(length));

    if ((uint32_t)txBuffer & 0x3U)  /* txBuffer address is not word alignment */
    {
        return;
    }

    dmaConfig.memStartAddr = (uint32_t)txBuffer;
    dmaConfig.memEndAddr = (uint32_t)txBuffer + ((1U + (length >> 2U)) << 2U);
    dmaConfig.periphStartAddr = (uint32_t)(&(I2Cx->DATA));
    dmaConfig.periphSelect = (I2C0 == I2Cx) ? DMA_PEPIRH_I2C0_TX : DMA_PEPIRH_I2C1_TX;
    dmaConfig.MEM2MEM = DISABLE;
    dmaConfig.memSize = DMA_MEM_SIZE_32BIT;
    dmaConfig.periphSize = DMA_PERIPH_SIZE_8BIT;
    dmaConfig.memIncrement = ENABLE;
    dmaConfig.periphIncrement = DISABLE;
    dmaConfig.direction = DMA_READ_FROM_MEM;
    dmaConfig.transferNum = length;
    dmaConfig.memByteMode = DMA_MEM_BYTE_MODE_4TIME;
    dmaConfig.circular = DISABLE;
    dmaConfig.channelPriority = DMA_PRIORITY_VERY_HIGH;
    dmaConfig.errorInterruptEn = ENABLE;
    dmaConfig.halfFinishInterruptEn = DISABLE;
    dmaConfig.finishInterruptEn = ENABLE;
    dmaConfig.channelEn = ENABLE;
    dmaConfig.callBack = callback;
    DMA_Init(DMAx, &dmaConfig);
}

/*!
 * @brief I2C slave start DMA receive.
 *        this will initialize DMA channel with the general DMA config
 *        make sure to enable i2c dma rx in I2C_Init before use the interface
 *        slave start DMA receive only when slave address match
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @param[in] DMAx: DMA module
 * @param[in] rxBuffer: memory start address
 * @param[in] length: length to receive
 * @param[in] callback: the DMA interrupt callback function
 * @return none
 */
void I2C_SlaveReceiveDMA(I2C_Type *I2Cx, DMA_ChannelType *DMAx, uint8_t *rxBuffer, uint16_t length, DeviceCallback_Type callback)
{
    DMA_ConfigType dmaConfig;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));
    DEVICE_ASSERT(IS_DMA_PERIPH(DMAx));
    DEVICE_ASSERT(IS_DMA_TRANSFER_NUM(length));

    dmaConfig.memStartAddr = (uint32_t)rxBuffer;
    dmaConfig.memEndAddr = (uint32_t)rxBuffer + length;
    dmaConfig.periphStartAddr = (uint32_t)(&(I2Cx->DATA));
    dmaConfig.periphSelect = (I2C0 == I2Cx) ? DMA_PEPIRH_I2C0_RX : DMA_PEPIRH_I2C1_RX;
    dmaConfig.MEM2MEM = DISABLE;
    dmaConfig.memSize = DMA_MEM_SIZE_8BIT;
    dmaConfig.periphSize = DMA_PERIPH_SIZE_32BIT;
    dmaConfig.memIncrement = ENABLE;
    dmaConfig.periphIncrement = DISABLE;
    dmaConfig.direction = DMA_READ_FROM_PERIPH;
    dmaConfig.transferNum = length;
    dmaConfig.memByteMode = DMA_MEM_BYTE_MODE_1TIME;
    dmaConfig.circular = DISABLE;
    dmaConfig.channelPriority = DMA_PRIORITY_VERY_HIGH;
    dmaConfig.errorInterruptEn = ENABLE;
    dmaConfig.halfFinishInterruptEn = DISABLE;
    dmaConfig.finishInterruptEn = ENABLE;
    dmaConfig.channelEn = ENABLE;
    dmaConfig.callBack = callback;
    DMA_Init(DMAx, &dmaConfig);
}

#ifdef I2C_USE_INTERRUPT_TRANSMIT

/*!
 * @brief I2C burst write with 7/10bit addressing by interrupt handler.
 *         related to variable: g_i2cTransmitInfo
 *         related to function: I2C_MasterSlaveIntHandler
 *         I2C interrupt must be enable
 *         Stop will directly generate if master get an nack
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @param[in] slaveAddr: slave 7/10bit address
 * @param[in] buff: the data buffer
 * @param[in] dataLength: the length to write
 * @param[in] stopEn: enable/diable i2c send stop after lastone byte for a possible re-start
 * @return i2c hardware status
 */
uint32_t I2C_MasterTransmitInt(I2C_Type *I2Cx, uint16_t slaveAddr, uint8_t *buff, uint32_t dataLength, ACTION_Type stopEn)
{
    uint8_t instance = I2C_INDEX(I2Cx);
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);
    DEVICE_ASSERT(NULL != buff);

    status |= I2C_WaitForReady(I2Cx);
    status |= I2C_Start(I2Cx);
    if (I2C_HW_STATUS_ERROR_NULL == status)
    {
        g_i2cTransmitInfo[instance].pointer = 0U;
        g_i2cTransmitInfo[instance].length = dataLength;
        g_i2cTransmitInfo[instance].buff = buff;
        g_i2cTransmitInfo[instance].stopEn = stopEn;
        g_i2cTransmitInfo[instance].wr = I2C_WRITE;
        g_i2cStatus[instance] = I2C_TRANSMIT_STATUS_ERROR_NULL;

        if (!I2C_IsADEXT(I2Cx))
        {
            I2C_TxEn(I2Cx);
            I2C_WriteDataReg(I2Cx, (((uint8_t)slaveAddr << 1U) | (uint8_t)I2C_WRITE));
        }
        else
        {
            I2C_SetInterrupt(I2Cx, DISABLE);
            status |= I2C_TransmitOneByte(I2Cx, ((I2C_ADDEXT_PRIMARY_BYTE_FIX + (slaveAddr >> 8U)) << 1U) | (uint8_t)I2C_WRITE);
            I2C_SetInterrupt(I2Cx, ENABLE);
            if (I2C_HW_STATUS_ERROR_NULL == status)
            {
                I2C_TxEn(I2Cx);
                I2C_WriteDataReg(I2Cx, (uint8_t)slaveAddr);
            }
        }
    }

    return status;
}

/*!
 * @brief I2C burst read with 7/10bit addressing by interrupt handler.
 *         related to variable: g_i2cTransmitInfo
 *         related to function: I2C_MasterSlaveIntHandler
 *         I2C interrupt must be enable
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @param[in] slaveAddr: slave 7/10bit address
 * @param[in] buff: the data buffer
 * @param[in] dataLength: the length to read
 * @return i2c hardware status
 */
uint32_t I2C_MasterReceiveInt(I2C_Type *I2Cx, uint16_t slaveAddr, uint8_t *buff, uint32_t dataLength)
{
    uint8_t instance = I2C_INDEX(I2Cx);
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);
    DEVICE_ASSERT(NULL != buff);

    status |= I2C_WaitForReady(I2Cx);
    status |= I2C_Start(I2Cx);
    if (I2C_HW_STATUS_ERROR_NULL == status)
    {
        g_i2cTransmitInfo[instance].pointer = 0U;
        g_i2cTransmitInfo[instance].length = dataLength;
        g_i2cTransmitInfo[instance].buff = buff;
        g_i2cTransmitInfo[instance].wr = I2C_READ;
        g_i2cStatus[instance] = I2C_TRANSMIT_STATUS_ERROR_NULL;

        if (!I2C_IsADEXT(I2Cx))
        {
            I2C_TxEn(I2Cx);
            I2C_WriteDataReg(I2Cx, (uint8_t)((slaveAddr << 1U) | (uint8_t)I2C_READ));
        }
        else
        {
            I2C_SetInterrupt(I2Cx, DISABLE);
            status |= I2C_TransmitOneByte(I2Cx, ((I2C_ADDEXT_PRIMARY_BYTE_FIX + (slaveAddr >> 8U)) << 1U) | (uint8_t)I2C_WRITE);
            status |= I2C_TransmitOneByte(I2Cx, (uint8_t)slaveAddr);
            status |= I2C_Restart(I2Cx);
            I2C_SetInterrupt(I2Cx, ENABLE);
            if (I2C_HW_STATUS_ERROR_NULL == status)
            {
                I2C_TxEn(I2Cx);
                I2C_WriteDataReg(I2Cx, ((I2C_ADDEXT_PRIMARY_BYTE_FIX + (slaveAddr >> 8U)) << 1U) | (uint8_t)I2C_READ);
            }
        }
    }

    return status;
}

/*!
 * @brief I2C slave burst write/read by interrupt handler.
 *         related to variable: g_i2cTransmitInfo
 *         related to function: I2C_MasterSlaveIntHandler
 *         I2C interrupt must be enable
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @param[in] buff: the data buffer
 * @param[in] dataLength: the length to write/read
 * @return none
 */
void I2C_SlaveTransmitReceiveInt(I2C_Type *I2Cx, uint8_t *buff, uint32_t dataLength)
{
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);
    DEVICE_ASSERT(NULL != buff);
    DEVICE_ASSERT(dataLength > 0U);

    g_i2cTransmitInfo[instance].pointer = 0U;
    g_i2cTransmitInfo[instance].length = dataLength;
    g_i2cTransmitInfo[instance].buff = buff;

    g_i2cStatus[instance] = I2C_TRANSMIT_STATUS_ERROR_NULL;
}

/*!
 * @brief get transmit status when use.
 *        I2C_MasterTransmitInt, I2C_MasterReceiveInt, I2C_SlaveTransmitReceiveInt
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @return i2c application status
 */
uint32_t I2C_GetTransmitReceiveStatus(I2C_Type *I2Cx)
{
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);

    return g_i2cStatus[instance];
}

/*!
 * @brief reset transmit status to I2C_TRANSMIT_STATUS_ERROR_NULL before using.
 *        I2C_MasterTransmitInt, I2C_MasterReceiveInt, I2C_SlaveTransmitReceiveInt
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @return none
 */
void I2C_ResetTransmitReceiveStatus(I2C_Type *I2Cx)
{
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);

    g_i2cTransmitInfo[instance].pointer = 0U;
    g_i2cTransmitInfo[instance].length = 0U;
    g_i2cTransmitInfo[instance].buff = NULL;

    g_i2cStatus[instance] = I2C_TRANSMIT_STATUS_ERROR_NULL;
}

/*!
 * @brief get transmitted length when use I2C_MasterTransmitInt, I2C_MasterReceiveInt, I2C_SlaveTransmitReceiveInt
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @return transmitted length
 */
uint32_t I2C_GetTransmittedReceivedLength(I2C_Type *I2Cx)
{
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);

    return g_i2cTransmitInfo[instance].pointer;
}

/*!
 * @brief Master interrupt handler routine when use.
 *        I2C_MasterTransmitInt, I2C_MasterReceiveInt
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @param[in] wparam: function parameter
 * @param[in] lparam: function parameter
 * @return none
 */
static void I2C_MasterIntHandler(I2C_Type *I2Cx, uint32_t wparam, uint32_t lparam)
{
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);

    if (I2C_IsARBLost(I2Cx))
    {
        I2C_ClearStatus0(I2Cx, I2C_STATUS0_ARBLOST_Msk);
        I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);
        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_ARBLOST;
        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
    }
    else
    {
        if (!(I2C_IsDMATxEnable(I2Cx) || I2C_IsDMARxEnable(I2Cx)))
        {
            if (I2C_IsTx(I2Cx))
            {
                if (0U == g_i2cTransmitInfo[instance].pointer)
                {
                    if (wparam & I2C_STATUS0_RACK_Msk)
                    {
                        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_NOT_MATCH;
                        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_NACK;
                        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
                        I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);
                        I2C_ClearStatus0(I2Cx, I2C_STATUS0_RACK_Msk);
                        I2C_SendStop(I2Cx);
                    }
                    else
                    {
                        if (0U < g_i2cTransmitInfo[instance].length)
                        {
                            if (I2C_WRITE == g_i2cTransmitInfo[instance].wr)
                            {
                                I2C_WriteDataReg(I2Cx, g_i2cTransmitInfo[instance].buff[g_i2cTransmitInfo[instance].pointer++]);
                            }
                            else
                            {
                                I2C_RxEn(I2Cx);
                                if (1U == g_i2cTransmitInfo[instance].length)
                                {
                                    I2C_SendNack(I2Cx);
                                }
                                else
                                {
                                    I2C_SendAck(I2Cx);
                                }
                                I2C_DumpReadDataReg(I2Cx);
                            }
                        }
                        else
                        {
                            g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_MATCHED;
                            g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
                            I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);
                            if (ENABLE == g_i2cTransmitInfo[instance].stopEn)
                            {
                                I2C_SendStop(I2Cx);
                            }
                        }
                    }
                }
                else if (g_i2cTransmitInfo[instance].length > g_i2cTransmitInfo[instance].pointer)
                {
                    if (wparam & I2C_STATUS0_RACK_Msk)
                    {
                        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_NACK;
                        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
                        I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);
                        I2C_ClearStatus0(I2Cx, I2C_STATUS0_RACK_Msk);
                        I2C_SendStop(I2Cx);
                    }
                    else
                    {
                        I2C_WriteDataReg(I2Cx, g_i2cTransmitInfo[instance].buff[g_i2cTransmitInfo[instance].pointer++]);
                    }
                }
                else
                {
                    if (wparam & I2C_STATUS0_RACK_Msk)
                    {
                        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_NACK;
                        I2C_ClearStatus0(I2Cx, I2C_STATUS0_RACK_Msk);
                    }
                    g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
                    I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);

                    if (ENABLE == g_i2cTransmitInfo[instance].stopEn)
                    {
                        I2C_SendStop(I2Cx);
                    }
                }
            }
            else
            {
                if (1U == g_i2cTransmitInfo[instance].length)
                {
                    g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
                    I2C_ReceiveLastOneByte(I2Cx, &g_i2cTransmitInfo[instance].buff[g_i2cTransmitInfo[instance].pointer++]);
                    I2C_SendAck(I2Cx);
                    I2C_SendStop(I2Cx);
                }
                else if ((g_i2cTransmitInfo[instance].length - 2U) > g_i2cTransmitInfo[instance].pointer)
                {
                    I2C_SendAck(I2Cx);
                    g_i2cTransmitInfo[instance].buff[g_i2cTransmitInfo[instance].pointer++] = I2C_ReadDataReg(I2Cx);
                }
                else if ((g_i2cTransmitInfo[instance].length - 2U) == g_i2cTransmitInfo[instance].pointer)
                {
                    I2C_SendNack(I2Cx);
                    g_i2cTransmitInfo[instance].buff[g_i2cTransmitInfo[instance].pointer++] = I2C_ReadDataReg(I2Cx);
                }
                else
                {
                    g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
                    I2C_ReceiveLastOneByte(I2Cx, &g_i2cTransmitInfo[instance].buff[g_i2cTransmitInfo[instance].pointer++]);
                    I2C_SendAck(I2Cx);
                    I2C_SendStop(I2Cx);
                }
            }
        }
        else
        {
            if (wparam & I2C_STATUS0_RACK_Msk)
            {
                g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_NACK;
                g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
                I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);
                I2C_ClearStatus0(I2Cx, I2C_STATUS0_RACK_Msk);
            }
        }
    }
}

/*!
 * @brief Slave interrupt handler routine when use I2C_SlaveWriteReceiveInt.
 *        slave BND flag is trig only address match
 *        master nack will not trig BND interrupt anymore
 *        slave nack will not trig BND interrupt anymore
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @param[in] wparam: function parameter
 * @param[in] lparam: function parameter
 * @return none
 */
static void I2C_SlaveIntHandler(I2C_Type *I2Cx, uint32_t wparam, uint32_t lparam)
{
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);

    if (!(I2C_IsDMATxEnable(I2Cx) || I2C_IsDMARxEnable(I2Cx)))
    {
        if (wparam & I2C_STATUS0_SAMF_Msk)
        {
            I2C_ClearStatus0(I2Cx, I2C_STATUS0_SAMF_Msk);
            g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_MATCHED;

            if (wparam & I2C_STATUS0_SRW_Msk)
            {
                if (g_i2cTransmitInfo[instance].length > 0U)
                {
                    I2C_WriteDataReg(I2Cx, g_i2cTransmitInfo[instance].buff[g_i2cTransmitInfo[instance].pointer]);
                }
            }
            else
            {
                if (g_i2cTransmitInfo[instance].length > 0U)
                {
                    I2C_SendAck(I2Cx);
                }
                else
                {
                    I2C_SendNack(I2Cx);
                    g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
                }
            }
        }
        else
        {
            if (wparam & I2C_STATUS0_SRW_Msk)
            {
                if (g_i2cTransmitInfo[instance].length - 1U >= g_i2cTransmitInfo[instance].pointer)
                {
                    g_i2cTransmitInfo[instance].pointer++;
                }
                if (wparam & I2C_STATUS0_RACK_Msk)
                {
                    if (!I2C_IsTxUF(I2Cx))
                    {
                        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_NACK;
                        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
                    }
                    I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);
                    I2C_ClearStatus0(I2Cx, I2C_STATUS0_RACK_Msk);
                }
                else
                {
                    if (g_i2cTransmitInfo[instance].length > g_i2cTransmitInfo[instance].pointer)
                    {
                        I2C_WriteDataReg(I2Cx, g_i2cTransmitInfo[instance].buff[g_i2cTransmitInfo[instance].pointer]);
                    }
                    else
                    {
                        if (I2C_IsStretch(I2Cx))
                        {
                            I2C_SetStretch(I2Cx, DISABLE);
                        }
                        if (!I2C_IsTxUF(I2Cx))
                        {
                            g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
                        }
                        I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);
                    }
                }
                if (I2C_IsTxUF(I2Cx))
                {
                    g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_TXUF;
                    I2C_ClearStatus1(I2Cx, I2C_STATUS1_TXUF_Msk);
                }
            }
            else
            {
                if (g_i2cTransmitInfo[instance].length > g_i2cTransmitInfo[instance].pointer)
                {
                    g_i2cTransmitInfo[instance].buff[g_i2cTransmitInfo[instance].pointer++] = I2C_ReadDataReg(I2Cx);
                    if (g_i2cTransmitInfo[instance].length == g_i2cTransmitInfo[instance].pointer)
                    {
                        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
                        I2C_SendNack(I2Cx);
                    }
                }
                else
                {
                    I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);
                }
            }
        }
    }
    else
    {
        if (wparam & I2C_STATUS0_SAMF_Msk)
        {
            I2C_ClearStatus0(I2Cx, I2C_STATUS0_SAMF_Msk);
            g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_MATCHED;
        }
        if (wparam & I2C_STATUS0_RACK_Msk)
        {
            I2C_ClearStatus0(I2Cx, I2C_STATUS0_RACK_Msk);
            g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_NACK;
            g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
        }
    }
}

/*!
 * @brief Master/Slave interrupt handler routine when use I2C_MasterTransmitInt, I2C_MasterReceiveInt, I2C_SlaveTransmitReceiveInt.
 *
 * @param[in] I2Cx: I2C module,x can be 0.1
 * @param[in] wparam: function parameter
 * @param[in] lparam: function parameter
 * @return none
 */
static void I2C_MasterSlaveIntHandler(void *device, uint32_t wparam, uint32_t lparam)
{
    I2C_Type *I2Cx = (I2C_Type *)device;
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);

    if (I2C_IsStart(I2Cx) && I2C_IsSSIntEnable(I2Cx))
    {
        I2C_ClearStartFlag(I2Cx);
        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_START_FLAG;
    }
    else if (I2C_IsStop(I2Cx) && I2C_IsSSIntEnable(I2Cx))
    {
        I2C_ClearStopFlag(I2Cx);
        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_STOP_FLAG;
        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
    }
    else
    {
        if (I2C_IsMaster(I2Cx))
        {
            I2C_MasterIntHandler(I2Cx, wparam, lparam);
        }
        else
        {
            I2C_SlaveIntHandler(I2Cx, wparam, lparam);
        }
    }
}

#endif /* I2C_USE_INTERRUPT_TRANSMIT */

/* =============================================  EOF  ============================================== */
